/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */

#ifndef __CPU_EDGE_ATOMIC_BLOCK_HH__
#define __CPU_EDGE_ATOMIC_BLOCK_HH__

#include <bitset>

#include "base/statistics.hh"
#include "cpu/edge/atomic/base_atomic_block.hh"

template <class Impl>
class AtomicEdgeBlock : public BaseAtomicEdgeBlock<Impl>
{
  public:
    /** Typedefs from Impl. */
    typedef typename Impl::CPU CPU;
    typedef typename Impl::DynInstPtr DynInstPtr;

    /// Block ID
    typedef TheISA::BlockID BlockID;
    typedef TheISA::HeaderInfo HeaderInfo;
    typedef TheISA::InstID InstID;
    typedef TheISA::ExitID ExitID;
    typedef TheISA::ConsumerType ConsumerType;
    typedef TheISA::ExitType ExitType;

    AtomicEdgeBlock(BlockID blockID, CPU* cpu);

    ~AtomicEdgeBlock();

    /** Returns name of this unit. */
    std::string name() const;

    Addr getBranchTarget() const { return branchTarget;}
    Addr getNextBlockPC() const { return nextBlockPC;}

    void setBranchTarget(Addr branch_pc) ;
    
    /** We set next block pc when we depack the header,
    *  but I still add this method to modify next block pc
    *  manually.
    */
    void setNextBlockPC(Addr next_block_pc);
    
  protected:
    uint32_t _mark;
    uint32_t _type;
    uint32_t _sMask;
    uint32_t _xFlags;
    uint32_t _chunkNum;

    std::bitset<TheISA::StoreMaskNum> _sMaskFlag;

    /** Branch target of this block. */
    Addr branchTarget;

    /** Start address of next block in program order*/
    Addr nextBlockPC;
    
    ExitID exitID;
    ExitType exitType;

    uint32_t _numReceivedStore;
    uint32_t _numReceivedWrite;
    uint32_t _numReceivedExit;

  public:

    uint32_t getChunkNum() const { return _chunkNum;}
    uint32_t getStoreMask() const { return _sMask;}

    /** Assemble header info from fetched header insts.
     *  Return true if everything is OK.
     *  Return false if the header is not compitable with TRIPS ISA
     *  which means an address with unaligned inst block boundary
     *  has been used.
    */
    bool depackHeader(HeaderInfo& info);

    bool isCompletionCondSatisfied();

    void incReceivedWrite(){ _numReceivedWrite++; }

    void incReceivedStore(){ _numReceivedStore++; }

    void setStoreMask( TheISA::LsID lsid ) 
    {
        assert(_sMaskFlag[lsid] == 0);
        
        _sMaskFlag.set(lsid); 
    }
    
    TheISA::LsID getLastStore( TheISA::LsID lsid );
    
    /** 
      * Get the first unexecuted store slot in LSID order.
      * Stores with such a LSID will behave like a memory blocker.
      */
    TheISA::LsID getMemBlockerLSID();

    TheISA::ExitID getExitID() const { return exitID; }

    TheISA::ExitType getExitType() const { return exitType; }

    uint32_t getStoreMask() { return _sMask; }

    void incReceivedExit() { assert(_numReceivedExit == 0); _numReceivedExit++; }

    void recordExitID(ExitID exit_id) { assert(_numReceivedExit == 1); exitID = exit_id;}

    void recordExitType(bool isCall, bool isReturn, bool isIndirect);

    uint32_t getNumReceivedWrite() const { return _numReceivedWrite; }

    uint32_t getNumReceivedStore() const { return _numReceivedStore; }

    uint32_t getNumReceivedExit() const { return _numReceivedExit; }

    /** Number of executed insts of this block. */ 
    int executedInsts;
};

#endif //__CPU_EDGE_ATOMIC_BLOCK_HH__
